/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.bef.core.action.modify;


import com.inspur.edp.bef.core.be.BEManager;
import com.inspur.edp.bef.spi.action.AbstractManagerAction;
import com.inspur.edp.cef.entity.accessor.base.AccessorComparer;
import com.inspur.edp.cef.entity.changeset.DeleteChangeDetail;
import com.inspur.edp.cef.entity.changeset.InnerUtil;
import com.inspur.edp.cef.entity.changeset.ModifyChangeDetail;
import com.inspur.edp.cef.entity.entity.IEntityData;
import com.inspur.edp.cef.entity.entity.IEntityDataCollection;
import com.inspur.edp.cef.entity.entity.IValueObjData;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

public class FullModifyAction extends AbstractManagerAction<IEntityData> {

  private final IEntityData data;
  private final BEManager manager;

  public FullModifyAction(BEManager manager, IEntityData data) {
    super(manager.getBEManagerContext());
    this.manager = manager;
    this.data = data;
  }

  @Override
  public void execute() {
    IEntityData org = manager.retrieve(data.getID()).getData();
    if (org == null) {
      throw new RuntimeException("编辑的数据不存在" + data.getID());
    }
    ModifyChangeDetail change = buildRootDataChange(org, data);
    if (change != null) {
      manager.modify(change);
    }
  }

  private ModifyChangeDetail buildRootDataChange(IEntityData org, IEntityData data) {
    ModifyChangeDetail change = buildDataChangeCore(org, data, new ArrayList<>(),
        Arrays.asList(data.getID()));
    return change;
  }

  private ModifyChangeDetail buildChildDataChange(IEntityData org,
      IEntityData data, List<String> nodePath, List<String> idPath) {
    if (org == null) {
      manager.retrieveDefaultChild(nodePath, idPath, data.getID());
    }
    List<String> childIdPath = new ArrayList<>(idPath);
    childIdPath.add(data.getID());
    return buildDataChangeCore(org, data, nodePath, idPath);
  }

  private ModifyChangeDetail buildDataChangeCore(IEntityData org,
      IEntityData data, List<String> nodePath, List<String> idPath) {
    ModifyChangeDetail change = new ModifyChangeDetail(data.getID());
    for (String prop : data.getPropertyNames()) {
      if ("ID".equalsIgnoreCase(prop)) {
        continue;
      }
      Object newValue = data.getValue(prop);
      if(newValue instanceof IEntityDataCollection){
        continue;
      }
      if (org == null || !equals(newValue, org.getValue(prop))) {
        change.putItem(prop, (newValue != null && newValue instanceof IValueObjData) ? InnerUtil
            .buildNestedChange(newValue) : newValue);
      }
    }

    for (Map.Entry<String, IEntityDataCollection> childEntry : data.getChilds().entrySet()) {
      IEntityDataCollection childs = data.getChilds().get(childEntry.getKey());
      IEntityDataCollection orgChilds =
          org == null ? null : org.getChilds().get(childEntry.getKey());

      if (orgChilds != null) {
        for (IEntityData orgChildData : orgChilds) {
          if (childs == null || childs.tryGet(orgChildData.getID()) == null) {
            change.addChildChangeSet(childEntry.getKey(),
                new DeleteChangeDetail(orgChildData.getID()));
          }
        }
      }

      if (childs != null) {
        List<String> childPath = new ArrayList<>(nodePath);
        childPath.add(childEntry.getKey());
        for (IEntityData childData : childs) {
          IEntityData orgChildData = orgChilds == null ? null : orgChilds.tryGet(childData.getID());
          ModifyChangeDetail childChangeDetail = buildChildDataChange(orgChildData, childData,
              childPath, idPath);
          if (childChangeDetail != null) {
            change.addChildChangeSet(childEntry.getKey(), childChangeDetail);
          }
        }
      }
    }

    if (change.getPropertyChanges().isEmpty() && (change.getChildChanges().isEmpty() || change
        .getChildChanges().values().stream().allMatch(item -> item.isEmpty()))) {
      return null;
    } else {
      return change;
    }
  }

  public static boolean equals(Object value1, Object value2) {
    if (value1 instanceof byte[] && value2 instanceof byte[]) {
      return AccessorComparer.equals((byte[]) value1, (byte[]) value2);
    } else {
      return AccessorComparer.equals(value1, value2);
    }
  }
}
